/**
 * Static class to aid in the handling of mouse operations.
 *
 * @author GabuEx, dawnmew
 * @since 1.0
 *
 * Licensed under the MIT License.
 *
 * Copyright (c) 2012 Equestrian Dreamers
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "MouseHelper.h"
#include "globals.h"
#include "Image.h"

const Uint32 ClickTimeThresholdMs = 250;
const int ClickDistanceThresholdPx = 5;
const Uint32 DoubleClickTimeThresholdMs = 250;

bool MouseHelper::clickPossible = false;
bool MouseHelper::clicked = false;
bool MouseHelper::doubleClickPossible = false;
bool MouseHelper::doubleClickWasPossible = false;
bool MouseHelper::doubleClicked = false;
bool MouseHelper::previousWasLeftButtonDown = false;
Vector2 MouseHelper::previousMousePosition = Vector2(0, 0);
bool MouseHelper::drawCursor = false;

Uint32 MouseHelper::initialLeftButtonDownTime = 0;
Vector2 MouseHelper::initialLeftButtonDownPosition = Vector2(0, 0);
Uint32 MouseHelper::initialClickTime = 0;

void MouseHelper::Init()
{
    clickPossible = false;
    clicked = false;
    doubleClickPossible = false;
    doubleClickWasPossible = false;
    doubleClicked = false;
    previousWasLeftButtonDown = false;
    previousMousePosition = Vector2(0, 0);
    drawCursor = false;
}

void MouseHelper::UpdateState(bool isLeftButtonDown, int mouseX, int mouseY, bool drawCursor)
{
    bool currentWasLeftButtonDown = isLeftButtonDown;

    previousMousePosition.SetX(mouseX);
    previousMousePosition.SetY(mouseY);

    UpdateTiming();

    if (!currentWasLeftButtonDown && previousWasLeftButtonDown)
    {
        // If a click is still possible now that the button has gone up,
        // and we weren't already expecting a double-click,
        // then we have a click, and we'll start considering the possibility
        // of a double-click. If on the other hand a double-click was expected,
        // then we now have a double-click.
        clicked = clickPossible && !doubleClickPossible;
        doubleClicked = clickPossible && doubleClickPossible;
        doubleClickPossible = clickPossible && !doubleClickPossible;
        clickPossible = false;

        if (doubleClickPossible)
        {
            initialClickTime = SDL_GetTicks();
        }
    }
    else if (currentWasLeftButtonDown && !previousWasLeftButtonDown)
    {
        initialLeftButtonDownTime = SDL_GetTicks();
        initialLeftButtonDownPosition = previousMousePosition;
        clickPossible = true;
        doubleClickWasPossible = doubleClickPossible;
    }

    previousWasLeftButtonDown = currentWasLeftButtonDown;
    MouseHelper::drawCursor = drawCursor;
}

void MouseHelper::UpdateTiming()
{
    if (clickPossible)
    {
        // We should only mark this as a click if the up
        // comes in fast enough following the down, and if the
        // cursor didn't move very far.
        clickPossible =
                SDL_GetTicks() - initialLeftButtonDownTime <= ClickTimeThresholdMs &&
                (previousMousePosition - initialLeftButtonDownPosition).Length() <= ClickDistanceThresholdPx;
    }

    if (doubleClickPossible)
    {
        // We should only mark this as a double-click if the
        // second click comes in fast enough following the first.
        doubleClickPossible = SDL_GetTicks() - initialClickTime <= DoubleClickTimeThresholdMs;
    }
}

bool MouseHelper::MouseOverRect(RectangleWH rectangle)
{
    return
        !clicked &&
        !previousWasLeftButtonDown &&
        IsMouseInRect(rectangle);
}

bool MouseHelper::MouseDownOnRect(RectangleWH rectangle)
{
    return
        previousWasLeftButtonDown &&
        IsMouseInRect(rectangle);
}

bool MouseHelper::PressedAndHeldAnywhere()
{
    return
        previousWasLeftButtonDown &&
        !clickPossible &&
        !doubleClickWasPossible &&
        IsMouseInViewport();
}

bool MouseHelper::DoublePressedAndHeldAnywhere()
{
    return
        previousWasLeftButtonDown &&
        !clickPossible &&
        doubleClickWasPossible &&
        IsMouseInViewport();
}

bool MouseHelper::ClickedOnRect(RectangleWH rectangle)
{
    return
        clicked &&
        IsMouseInRect(rectangle);
}

bool MouseHelper::ClickedAnywhere()
{
    return
        clicked &&
        IsMouseInViewport();
}

bool MouseHelper::DoubleClickedOnRect(RectangleWH rectangle)
{
    return
        doubleClicked &&
        IsMouseInRect(rectangle);
}

bool MouseHelper::DoubleClickedAnywhere()
{
    return
        doubleClicked &&
        IsMouseInViewport();
}

Vector2 MouseHelper::GetMousePosition()
{
    return previousMousePosition;
}

void MouseHelper::HandleClick()
{
    clicked = false;
}

void MouseHelper::HandleDoubleClick()
{
    doubleClicked = false;
}

bool MouseHelper::IsMouseInRect(RectangleWH rectangle)
{
    return
        previousMousePosition.GetX() >= rectangle.GetX() &&
        previousMousePosition.GetX() <= rectangle.GetX() + rectangle.GetWidth() &&
        previousMousePosition.GetY() >= rectangle.GetY() &&
        previousMousePosition.GetY() <= rectangle.GetY() + rectangle.GetHeight();
}

bool MouseHelper::IsMouseInViewport()
{
    return IsMouseInRect(RectangleWH(0, 0, gScreenWidth, gScreenHeight));
}
